
#ifndef Velyn_ObjLoader_h
#define Velyn_ObjLoader_h

namespace Velyn {

    class ObjectLoader {

    private:
        
        enum Type {
            VERTEX, TEXTURE, NORMAL, FACE, NOT_A_TYPE
        };
        
        Type checkBuffer(const char* buffer) {
            if(buffer[0] == 'v' and buffer[1] == ' ') {
                return VERTEX;
            } else if(buffer[0] == 'v' and buffer[1] == 't') {
                return TEXTURE;
            } else if(buffer[0] == 'v' and buffer[1] == 'n') {
                return NORMAL;
            } else if(buffer[0] == 'f' and buffer[1] == ' ') {
                return FACE;
            } 
            return NOT_A_TYPE;
        }
        
    public:
        
        ObjLoader() {}
        
        ~ObjLoader() {}
        
        Mesh* loadFile(const char* filename) {
            Mesh* mesh = new Mesh();
            FILE* file = std::fopen(filename, "r");
            
            char buffer[255];
            
            float x, y, z, u, v;
            
            VertexIndex vertexIndex[3];
            TextureIndex textureIndex[3];
            NormalIndex normalIndex[3];
            
            if(file) {
                while(std::fgets(buffer, 255, file) != NULL) {
                    Type type;
                    
                    type = checkBuffer(buffer);
                    
                    switch(type) {
                        case VERTEX: {
                            std::sscanf(buffer, "v %f %f %f", &x, &y, &z);
                            mesh->addVertex(x, y, z);
                        } break;
                            
                        case TEXTURE: {
                            std::sscanf(buffer, "vt %f %f", &u, &v);
                            mesh->addTexture(u, v);
                        } break;
                            
                        case NORMAL: {
                            std::sscanf(buffer, "vn %f %f %f", &x, &y, &z);
                            mesh->addNormal(x, y, z);
                        } break;
                            
                        case FACE: {
                            
                            //FIXME: this may not read in the '/' in correctly
                            std::sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d", 
                                        &vertexIndex[0], &textureIndex[0], &normalIndex[0],
                                        &vertexIndex[1], &textureIndex[1], &normalIndex[1],
                                        &vertexIndex[2], &textureIndex[2], &normalIndex[2]);
                            
                            mesh->addTriangle(vertexIndex[0] - 1, vertexIndex[1] - 1, vertexIndex[2] - 1);
                            
                            mesh->addFace(vertexIndex[0] - 1, normalIndex[0] - 1);
                            mesh->addFace(vertexIndex[1] - 1, normalIndex[1] - 1);
                            mesh->addFace(vertexIndex[2] - 1, normalIndex[2] - 1);
                        } break;
                            
                        case NOT_A_TYPE: {} break;
                            
                        default: {} break;
                    }
                }
            } else {
                std::printf("File does not exist!\n");
            }
            
            std::fclose(file);
            
            return mesh;
        }
        
        bool loadFile(const char* filename, Mesh* mesh) {
            bool result;
            
            // Mesh* mesh = new Mesh();
            FILE* file = std::fopen(filename, "r");
            
            char buffer[255];
            
            float x, y, z, u, v;
            
            VertexIndex vertexIndex[3];
            TextureIndex textureIndex[3];
            NormalIndex normalIndex[3];
            
            if(file) {
                while(std::fgets(buffer, 255, file) != NULL) {
                    Type type;
                    
                    type = checkBuffer(buffer);
                    
                    switch(type) {
                        case VERTEX: {
                            std::sscanf(buffer, "v %f %f %f", &x, &y, &z);
                            mesh->addVertex(x, y, z);
                        } break;
                            
                        case TEXTURE: {
                            std::sscanf(buffer, "vt %f %f", &u, &v);
                            mesh->addTexture(u, v);
                        } break;
                            
                        case NORMAL: {
                            std::sscanf(buffer, "vn %f %f %f", &x, &y, &z);
                            mesh->addNormal(x, y, z);
                        } break;
                            
                        case FACE: {
                            
                            //FIXME: this may not read in the '/' in correctly
                            std::sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d", 
                                        &vertexIndex[0], &textureIndex[0], &normalIndex[0],
                                        &vertexIndex[1],&textureIndex[1], &normalIndex[1],
                                        &vertexIndex[2], &textureIndex[2], &normalIndex[2]);
                            
                            mesh->addTriangle(vertexIndex[0] - 1, vertexIndex[1] - 1, vertexIndex[2] - 1);
                            
                            mesh->addFace(vertexIndex[0] - 1, normalIndex[0] - 1);
                            mesh->addFace(vertexIndex[1] - 1, normalIndex[1] - 1);
                            mesh->addFace(vertexIndex[2] - 1, normalIndex[2] - 1);
                        } break;
                            
                        case NOT_A_TYPE: {} break;
                            
                        default: {} break;
                    }
                }
                result = true;
            } 
            
            else {
                result = false;
            }
            
            std::fclose(file);
            
            return result;
        }
    
    };
    
}

#endif
